package com.newear.base.http;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.util.ArrayMap;

import com.newear.util.MyLogger;
import com.newear.util.MySharedPreferences;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.FormEncodingBuilder;
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.MultipartBuilder;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody;

import org.json.JSONObject;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.util.concurrent.TimeUnit;

/**
 * OKHttp工具类
 * 
 * @author josan_tang 2016/07/19
 */
public class OKHttpClientManager {

	private final static String TAG = "OKHttpClientManager";

	/**
	 * Handler对象
	 */
	private static Handler mDelivery  = new Handler(Looper.getMainLooper());

	/**
	 * 默认链接超时时间，90s
	 */
	private static final int timeoutConnection = 90000;
	
	/**
	 * 默认读取超时时间，90s
	 */
	private final static int timeoutRead = 90000;

	/**
	 * 构造方法，抛错，提示不能创建OKhttpClientManager对象，全部使用static调用
	 */
	public OKHttpClientManager() {

	}

	/**
	 * 得到OkHttpClient对象
	 * @return
	 */
	private static OkHttpClient getOkHttpClient() {
		OkHttpClient mOkHttpClient = new OkHttpClient();
		//设置OKHttpClient对象属性
		mOkHttpClient.setConnectTimeout(timeoutConnection, TimeUnit.MILLISECONDS);
		mOkHttpClient.setReadTimeout(timeoutRead, TimeUnit.MILLISECONDS);
		return mOkHttpClient;
	}

	/***************** 底层的实现方法 ***************/

	/**
	 * Get同步方式得到Response
	 * 
	 * @param url
	 *            请求url
	 *
	 * @return 请求结果Response
	 */
	private static Response _getResponse(Context context, String url) {
		OkHttpClient mOkHttpClient = getOkHttpClient();
		String token = MySharedPreferences.getInstance().getToken(context);
		// 创建Request
		final Request request = new Request.Builder().url(url)
				.addHeader("Cookie", "jwt=" + token).build();
		// 创建Call
		Call call = mOkHttpClient.newCall(request);
		try {
			// 发起请求，得到Response
			Response response = call.execute();
			return response;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * Get同步方式得到String
	 * 
	 * @param url 
	 *            请求url
	 *
	 * @return 请求结果String
	 */
	private static String _getString(Context context, String url) {
		Response response = _getResponse(context, url);
		try {
			if (response != null) {
				String result = response.body().string();
				return result;
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
		return null;
	}

	/**
	 * Get异步方式请求,证书
	 * 
	 * @param url
	 *            请求url
	 * @param callback
	 *            回调对象
	 */
	private static void _getAsyn(String url, ResultCallback<Object> callback) {
		// 创建Request对象
		final Request request = new Request.Builder().url(url).build();
		deliveryResult(request, callback);
	}

	/**
	 * Post同步方式得到Response
	 * 
	 * @param url
	 *            请求url
	 * @param params
	 *            post参数
	 *
	 * @return 请求结果Response
	 */
	private static Response _postResponse(String url, ArrayMap<String, String> params) {
		OkHttpClient mOkHttpClient = getOkHttpClient();

		// 构建Request
		Request request = buildPostRequest(url, params);

		// 得到Response
		Response response;
		try {
			response = mOkHttpClient.newCall(request).execute();
			return response;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}

	}

	private static Response _jsonResponse(String url, ArrayMap<String, String> params) {
		OkHttpClient mOkHttpClient = getOkHttpClient();

		// 构建Request
		Request request = buildJsonRequest(url, params);
		// 得到Response
		Response response;
		try {
			response = mOkHttpClient.newCall(request).execute();
			return response;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}

	}

	private static Response _jsonResponse(Context context, String url, String json) {
		OkHttpClient mOkHttpClient = getOkHttpClient();

		// 构建Request
		Request request = buildJsonRequest(context, url, json);
		// 得到Response
		Response response;
		try {
			response = mOkHttpClient.newCall(request).execute();
			return response;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}

	}

	/**
	 * Post同步方式得到String
	 * 
	 * @param url
	 *            请求url
	 * @param params
	 *            post参数
	 *
	 * @return 请求结果String
	 */
	private static String _postString(String url, ArrayMap<String, String> params) {
		Response response = _postResponse(url, params);
		String result = null;
		try {
			if (response != null) {
				ResponseBody responseBody = response.body();
				if (responseBody != null) {
					result = responseBody.string();
					return result;
				}
			}
		} catch (IOException e) {
			return null;
		}
		return null;
	}

	private static String _postJson(String url, ArrayMap<String, String> params) {
		Response response = _jsonResponse(url, params);
		String result = null;
		try {
			if (response != null) {
				ResponseBody responseBody = response.body();
				if (responseBody != null) {
					result = responseBody.string();
					return result;
				}
			}
		} catch (IOException e) {
			return null;
		}
		return null;
	}

	private static String _postJson(Context context, String url, String json) {
		Response response = _jsonResponse(context, url, json);
		String result = null;
		try {
			if (response != null) {
				ResponseBody responseBody = response.body();
				if (responseBody != null) {
					result = responseBody.string();
					return result;
				}
			}
		} catch (IOException e) {
			return null;
		}
		return null;
	}

	/**
	 * Post异步方式请求
	 * 
	 * @param url
	 *            请求url
	 * @param callback
	 *            回调对象
	 * @param params
	 *            post参数，Param封装
	 *  	                  证书，如果不需要则设置为空
	 */
	private static void _postAsyn(String url, ResultCallback<Object> callback, ArrayMap<String, String> params) {
		// 构建Request对象
		Request request = buildPostRequest(url, params);
		// 得到并分发结果
		deliveryResult(request, callback);
	}

	/**
	 * 同步基于post的文件上传,只支持单个文件，且不带参数
	 * @param url
	 * @param file
	 * @param fileKey
	 * @return
	 */
	private static String _postUpload(String url, File file, String fileKey, ArrayMap<String, String> params) {
        try {
            OkHttpClient mOkHttpClient = getOkHttpClient();
            Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, params);
            Response response = mOkHttpClient.newCall(request).execute();;
            String result = null;
            if (response != null) {
                ResponseBody responseBody = response.body();
                if (responseBody != null) {
                    result = responseBody.string();
                    return result;
                }
            }
        } catch (IOException e) {
            return null;
        }
        return null;
	}

	/**
	 * 异步基于post的文件上传,支持多个文件
	 * @param url
	 * @param files
	 * @param fileKeys
	 * @param param
	 * @return
	 */
	private static void _postUploadAsyn(String url, ResultCallback<Object> callback, File[] files, String[] fileKeys, ArrayMap<String, String> param) {
		Request request = buildMultipartFormRequest(url, files, fileKeys, param);
		deliveryResult(request, callback);
	}

	/**
	 * 异步基于post的文件上传,只支持单个文件，且不带参数
	 * @param url
	 * @param file
	 * @param fileKey
	 * @return
	 */
	private static void _postUploadAsyn(String url, ResultCallback<Object> callback, File file, String fileKey) {
		Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, null);
		deliveryResult(request, callback);
	}

	/**
	 * 异步基于post的文件上传,只支持单个文件，支持带参数
	 * @param url
	 * @param file
	 * @param fileKey
	 * @param params
	 * @return
	 */
	private static void _postUploadAsyn(String url, ResultCallback<Object> callback, File file, String fileKey, ArrayMap<String, String> params) {
		Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, params);
		deliveryResult(request, callback);
	}

	/**
	 * 根据url得到Bitmap
	 * @param url
	 */
	private static void downloadImgAsyn(String url, final ResultCallback<Object> callback) {
		OkHttpClient mOkHttpClient = getOkHttpClient();
		final Request request;
		try {
			request = new Request.Builder()
					.url(url)
					.build();
		}catch (Exception e) {
			return;
		}
		Call call = mOkHttpClient.newCall(request);
		call.enqueue(new Callback() {
			@Override
			public void onFailure(Request request, IOException e) {
				sendFailedStringCallback(request, callback, e);
			}

			@Override
			public void onResponse(Response response) throws IOException {
				InputStream is = null;
				FileOutputStream fos = null;
				try {
					is = response.body().byteStream();
					Bitmap bitmap = BitmapFactory.decodeStream(is);
					sendSuccessResultCallback(bitmap, callback);
				} catch (IOException e) {
					sendFailedStringCallback(request, callback, e);
				} finally {
					try {
						if (is != null) is.close();
					} catch (IOException e) {
					}
					try {
						if (fos != null) fos.close();
					} catch (IOException e) {
					}
				}
			}
		});
	}


	/***************** 对外公开的方法 ***************/

	/**
	 * Get同步方式得到String
	 * 
	 * @param url
	 *            请求Url
	 *
	 * @return 请求结果String
	 */
	public static String getString(Context context, String url) {
		return _getString(context, url);
	}

	/**
	 * Get异步方式请求
	 * 
	 * @param url
	 *            请求url
	 * @param callback
	 *            回调对象
	 */
	public static void getAsyn(String url, ResultCallback<Object> callback) {
		_getAsyn(url, callback);
	}

	/**
	 * Post同步方式得到Response
	 * 
	 * @param url
	 *            请求url
	 * @param params
	 *            post参数
	 *
	 * @return 请求结果Response
	 */
	public static Response postResponse(String url, ArrayMap<String, String> params) {
		return _postResponse(url, params);
	}

	/**
	 * Post同步方式得到String
	 * 
	 * @param url
	 *            请求url
	 * @param params
	 *            post参数
	 *
	 * @return 请求结果String
	 */
	public static String postString(String url, ArrayMap<String, String> params) {
		return _postString(url, params);
	}

	public static String postJson(String url, ArrayMap<String, String> params) {
		return _postJson(url, params);
	}

	public static String postJson(Context context, String url, String json) {
		return _postJson(context, url, json);
	}

	/**
	 * Post异步方式请求
	 * 
	 * @param url
	 *            请求url
	 * @param callback
	 *            回调对象
	 * @param params
	 *            post参数,Param封装
	 */
	public static void postAsyn(String url, final ResultCallback<Object> callback, ArrayMap<String, String> params) {
		_postAsyn(url, callback, params);
	}
	/**
	 * 同步，单文件上传，不支持携带参数
	 * @param url
	 * @param file
	 * @param fileKey
	 * @return
	 */
	public static String postUpload(String url, File file, String fileKey, ArrayMap<String, String> param) {
		return _postUpload(url, file, fileKey, param);
	}

	/**
	 * 异步，单文件上传，不支持携带参数
	 * @param url
	 * @param file
	 * @param fileKey
	 * @return
	 */
	public static void postUpload(String url, ResultCallback<Object> callback, File file, String fileKey) {
		_postUploadAsyn(url, callback, file, fileKey);
	}

	/**
	 * 异步，单文件上传，支持携带参数
	 * @param url
	 * @param file
	 * @param fileKey
	 * @param param
	 * @return
	 */
	public static void postUpload(String url, ResultCallback<Object> callback, File file, String fileKey , ArrayMap<String, String> param) {
		_postUploadAsyn(url, callback, file, fileKey, param);
	}

	/**
	 * 异步，多文件上传，支持携带参数
	 * @param url
	 * @param files
	 * @param fileKeys
	 * @param param
	 * @return
	 */
	public static void postUpload(String url, ResultCallback<Object> callback, File[] files, String[] fileKeys, ArrayMap<String, String> param) {
		_postUploadAsyn(url, callback, files, fileKeys, param);
	}

	/**
	 * 异步下载图片
	 * @param url
	 */
	public static void downImgAysn(String url, ResultCallback<Object> callback) {
		downloadImgAsyn(url, callback);
	}

	/*************** 辅助类 **********************/

	/**
	 * 请求结果回调类
	 * 
	 * @author josan_tang
	 * 
	 * @param <T>
	 */
	public static abstract class ResultCallback<T> {

		/**
		 * 请求成功时的回调方法
		 * 
		 * @param response
		 */
		public abstract void onResponse(T response);

		/**
		 * 请求失败时的回调方法
		 * 
		 * @param request
		 * @param exception
		 */
		public abstract void onError(Request request, Exception exception);

	}

	/**************** 辅助方法 *************************/

	/**
	 * 分发结果
	 * 
	 * @param request
	 *            请求对象
	 * @param callback
	 *            回调对象
	 */
	private static void deliveryResult(Request request, final ResultCallback<Object> callback) {
		OkHttpClient mOkHttpClient = getOkHttpClient();
		mOkHttpClient.setConnectTimeout(90, TimeUnit.SECONDS);
		mOkHttpClient.newCall(request).enqueue(new Callback() {

			@Override
			public void onResponse(Response response) throws IOException {
				// TODO Auto-generated method stub
				final String result = response.body().string();
				sendSuccessResultCallback(result, callback);
			}

			@Override
			public void onFailure(Request request, IOException exception) {
				// TODO Auto-generated method stub
				sendFailedStringCallback(request, callback, exception);
			}
		});
	}

	/**
	 * 发送请求成功的结果
	 * 
	 * @param object
	 * @param callback
	 */
	private static void sendSuccessResultCallback(final Object object, final ResultCallback<Object> callback) {
		mDelivery.post(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				if (callback != null) {
					callback.onResponse(object);
				}
			}
		});
	}

	/**
	 * 发送请求失败的结果
	 * 
	 * @param request
	 * @param callback
	 * @param exception
	 */
	private static void sendFailedStringCallback(final Request request, final ResultCallback<Object> callback, final Exception exception) {
		mDelivery.post(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				if (callback != null) {
					callback.onError(request, exception);
				}
			}

		});
	}

	/**
	 * 构建Post方式请求的Request
	 * 
	 * @param url
	 *            请求url
	 * @param params
	 *            post请求参数
	 *            
	 * @return Request对象
	 */
	private static Request buildPostRequest(String url, ArrayMap<String, String> params) {

		FormEncodingBuilder builder = new FormEncodingBuilder();
		
		for(int i = 0;i < params.size(); i++) {
			builder.add(params.keyAt(i), params.valueAt(i));
		}
		
		RequestBody requestBody = builder.build();

		Request request = new Request.Builder().url(url).post(requestBody)
				.build();

		return request;
	}

	/**
	 * 构建Post方式请求的Request
	 *
	 * @param url
	 *            请求url
	 * @param params
	 *            post请求参数
	 *
	 * @return Request对象
	 */
	private static Request buildJsonRequest(String url, ArrayMap<String, String> params) {
		try {
			JSONObject jsonObject = new JSONObject();
			for(int i = 0;i < params.size(); i++) {
				jsonObject.put(params.keyAt(i), params.valueAt(i));
			}
			MyLogger.e("jsonObj:" + jsonObject.toString());
			MediaType JSON = MediaType.parse("application/json; charset=utf-8");
			RequestBody requestBody = RequestBody.create(JSON, jsonObject.toString());
			String token = params.get("token");
			final Request request = new Request.Builder()
					.url(url)
					.addHeader("Cookie", "jwt=" + token)
					.post(requestBody)
					.build();
			return request;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 构建Post方式请求的Request
	 *
	 * @return Request对象
	 */
	private static Request buildJsonRequest(Context context, String url, String json) {
		try {
			MyLogger.e("jsonObj:" + json);
			MediaType JSON = MediaType.parse("application/json; charset=utf-8");
			RequestBody requestBody = RequestBody.create(JSON, json);
			String token = MySharedPreferences.getInstance().getToken(context);
			final Request request = new Request.Builder()
					.url(url)
					.addHeader("Cookie", "jwt=" + token)
					.post(requestBody)
					.build();
			return request;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 构建复杂的Request,主要用于文件上传
	 * @param url
	 * @param files
	 * @param fileKeys
	 * @param params
	 * @return
	 */
	private static Request buildMultipartFormRequest(String url, File[] files,  String[] fileKeys, ArrayMap<String, String> params) {
		MultipartBuilder builder = new MultipartBuilder().type(MultipartBuilder.FORM);
		if (params != null) {
			for(int i = 0;i < params.size(); i++) {
				builder.addPart(Headers.of("Content-Disposition", "form-data;name=\"" + params.keyAt(i) + "\""), RequestBody.create(null, params.valueAt(i)));
			}
		}
		if (files != null) {
			RequestBody fileBody = null;
			for (int i=0; i<files.length; i++) {
				File file = files[i];
				String fileName = file.getName();
				MyLogger.e("file name:" + fileName + ",file size:" + (file.length() / 1024.0 / 1024) + "mb");
				fileBody = RequestBody.create(MediaType.parse(guessMimeType(fileName)), file);
				//根据文件名，设置contentType
				builder.addPart(Headers.of("Content-Disposition", "form-data;name=\"" + fileKeys[i] + "\";filename=\"" + fileName +"\""), fileBody);
			}
		}
		RequestBody requestBody = builder.build();
        String token = params.get("token");
		return new Request.Builder()
				.url(url)
                .addHeader("Cookie", "jwt=" + token)
				.post(requestBody)
				.build();
	}

	/**
	 * 猜测path对应文件类型
	 * @param path
	 * @return
	 */
	private static String guessMimeType(String path) {
		FileNameMap fileNameMap = URLConnection.getFileNameMap();
		String contentTypeFor = fileNameMap.getContentTypeFor(path);
		if (contentTypeFor == null) {
			contentTypeFor = "application/octet-stream";
		}
		return contentTypeFor;
	}

}
